iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0
自我挑戰組

玩轉 React 從0到1系列 第 11

【Day 11】穿越 ES6 邁向 ES7 的 async/await

  • 分享至 

  • xImage
  •  

前言

從 ES5 的 Callback 以及它遺留下來的問題 Callback Hell ,到 ES6 的 Promise 以及 Generator,這之中都有所改進,但又往往留下了一點問題,終於到了 ES7 的 async/await,它更優雅的實現 Javascript 的異步操作,讓異步操作可以以同步化的方式進行

Async 函數是什麼?

一句話,它就是 Generator 的 語法糖

const fs = require('fs');
const readFile = function(filename) {
  return new Promise(function (resolve, reject) {
    fs.readFile(filename, (error, data) => {
      return err ? reject(err) : resolve(data);
    })
  })
};
const gen = function* () {
  const file1 = yield readFile('/file/a1');
  const file2 = yield readFile('/file/b2');
  console.log(file1.toString());
  console.log(file2.toString());
}
// 上方這段程式碼寫成 async 函數,就是下面這樣
const AsyncMethodReadFile = async function() {
  const file1 = await readFile('/file/a1');
  const file2 = await readFile('/file/b2');
  console.log(file1.toString());
  console.log(file2.toString());
}

由上面例子就可以發現,其實 async = function*,而 yield = await

async function f() {
  return 'Hello Async';
}
f().then(a => console.log(a));   // f()返回值是 promise
//Hello Async

async function f() {
  return await 123;   // 因為不是接 promise 所以立即 resolve,回傳數值
}
f().then(a => console.log(a)); 

ES7 的 async 式表示函數都是異步的,返回值都是 Promise,所以可以接續使用 then 添加 callback,
而 await 可以理解為 async wait,一定要寫在 async 內部,主要的作用就是用來等待 Promise 的狀態被 resolved,正常狀況下,await 後面接的是一個 Promise,如果不是,則會立即轉成一個 resolve 的 Promise。

Async 相較於 Promise

Async 中的異常處理

在 Promise 的異常處理中,catch 並沒有解決 Callback 那個時代的問題(try-catch無法捕捉異步的異常,需要寫很多個 debugger 去追蹤),Promise 的 catch函數依舊不夠完整,難以獲得完整的異常訊息,以下面例子為例:

function getData() {
  try {
    requestData()
      .then(result => {
        const data = JSON.parse(result);
      })
      .catch((error) => {
          console.log(error);
      })
  } catch (error) {
    console.log(error);
  }
}

在這段代碼中,try-catch可以捕捉到 Promise 內部的錯誤卻無法捕捉到 JSON.parse 拋出的錯誤,那如果要處理這項問題還需要添加 try-catch 去處理邏輯,如果今天是在更大型的項目,邏輯可能就會很混亂也會有不少冗余的程式。
以下方程式為例,如果是用 async/await 則可以使 try/catch 捕捉到同步和異步的錯誤;順帶一提,由於 await 後面接的 Promise 運行結果可能是 rejected,所以最好把 await 放在 try/catch中。

async function getData() {
  try {
    const data = JSON.parse(await requestData());
  } catch (error) {
    console.log(error);
  }
}

多個並行的 await

下方程式(第一則)是通過 await 去獲得學生資料,其中 getName()getLevel是兩個獨立的異步操作,但是 getLevel卻要等 getName resolved後才能操作,這時候就會影響到效能,這裡要注意 async說到底還是異步的,不要因為它有類似同步的操作而濫用它。

async function retriveUserDetail(url) {
  let user = await getUser(url);
  let name = await getName(user);
  let level = await getLevel(user);
  return [user,name,level]
}
// 以下兩種寫法 `getName` 跟 `getLevel`都是同時觸發
// 更正版寫法
async function retriveUserDetailCorrect(url) {
  let user = await getUser(url);
  let nameTemporarily = getName(user);
  let levelTemporarily = getLevel(user);
  let name = await nameTemporarily;
  let level = await levelTemporarily;
  return [user, roles, level];
}
// 更正版寫法
async function retriveUserDetailCorrect(url) {
  let user = await getUser(url);
  let [name, level] = await Promise.all([getName(user), getLevel(user)]);
  return [user, roles, level];
}

結論

  • 介紹了 async/await

/images/emoticon/emoticon25.gif


可能會補上 generator+promise


上一篇
【Day 10】關於ES6 的 Promise
下一篇
【Day 12】React 基礎安裝
系列文
玩轉 React 從0到130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言